﻿// Copyright (c) 2009, Heiko Wundram (modelnine@h8me.eu).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
// conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
//   in the documentation and/or other materials provided with the distribution.
// * Neither the name of Heiko Wundram (h8me.eu) nor the names of its contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace eu.h8me.Parsing.ParserLib {

    /// <summary>
    /// Helper interface to represent a stored grammar.
    /// </summary>
    public interface IStoredGrammar {

        /// <summary>
        /// Fetch the starting symbol that was used to create this grammar state machine.
        /// </summary>
        string Start {
            get;
        }

        /// <summary>
        /// List the symbols bound to the current stored grammar.
        /// </summary>
        IEnumerable<StoredSymbol> Symbols {
            get;
        }

        /// <summary>
        /// Fetch state set dictionary of grammar.
        /// </summary>
        IEnumerable<Dictionary<StoredSymbol, StoredStateAction>> States {
            get;
        }

    }

    /// <summary>
    /// Helper class used to store a symbol with productions.
    /// </summary>
    public sealed class StoredSymbol {

        /// <summary>
        /// Helper enumeration to declare symbol types.
        /// </summary>
        public enum SymbolType {
            Terminal,
            NonTerminal
        };

        // Data.
        private string _Name;
        private SymbolType _Type;
        private int? _Precedence;
        private Symbol.Associativity? _Associativity;
        private List<StoredProduction> _Productions;

        /// <summary>
        /// Initialize a new stored symbol object from the passed parameters.
        /// </summary>
        /// <param name="name">Name of the symbol.</param>
        /// <param name="type">Type of the symbol.</param>
        /// <param name="prec">Precedence of the symbol (or null if unset).</param>
        /// <param name="assoc">Associativity of the symbol (or null if unset).</param>
        /// <param name="prods">Productions bound to this symbol.</param>
        public StoredSymbol(string name, SymbolType type, int? prec, Symbol.Associativity? assoc) {
            // Store data.
            _Name = name;
            _Type = type;
            _Precedence = prec;
            _Associativity = assoc;
        }

        /// <summary>
        /// Fetch name of symbol.
        /// </summary>
        internal string Name {
            get {
                // Retrieve name of symbol.
                return _Name;
            }
        }

        /// <summary>
        /// Fetch type of symbol.
        /// </summary>
        internal SymbolType Type {
            get {
                // Return type.
                return _Type;
            }
        }

        /// <summary>
        /// Symbol precedence.
        /// </summary>
        internal int? Precedence {
            get {
                // Fetch symbol precedence.
                return _Precedence;
            }
        }

        /// <summary>
        /// Symbol associativity.
        /// </summary>
        internal Symbol.Associativity? Associativity {
            get {
                // Fetch symbol associativity.
                return _Associativity;
            }
        }

        /// <summary>
        /// Stored productions for symbol.
        /// </summary>
        internal List<StoredProduction> Productions {
            get {
                // Fetch list of symbol productions.
                Debug.Assert(_Type == SymbolType.NonTerminal, "Trying to fetch productions on terminal?");
                return _Productions ?? new List<StoredProduction>();
            }
        }

        /// <summary>
        /// Attach the specified production to the current production list.
        /// </summary>
        /// <param name="prod">Production to attach.</param>
        public void Attach(StoredProduction prod) {
            // Diagnostics.
            Debug.Assert(_Type == SymbolType.NonTerminal, "Attaching production to terminal?");

            // Create production list and attach.
            if( _Productions == null )
                _Productions = new List<StoredProduction>();
            _Productions.Add(prod);
        }

    }

    /// <summary>
    /// Helper class to store a production as a list of stored symbol references.
    /// </summary>
    public sealed class StoredProduction {

        // Data.
        private int? _Precedence;
        private List<StoredSymbol> _Symbols;

        /// <summary>
        /// Initialize a new stored production from the passed symbols.
        /// </summary>
        /// <param name="symbs">Symbols to bind to the current production.</param>
        public StoredProduction(int? prec, List<StoredSymbol> symbs) {
            // Store data.
            _Precedence = prec;
            _Symbols = symbs;
        }

        /// <summary>
        /// Fetch the precedence set up for this stored object.
        /// </summary>
        internal int? Precedence {
            get {
                // Fetch precedence.
                return _Precedence;
            }
        }

        /// <summary>
        /// Fetch list of stored symbols making this production.
        /// </summary>
        internal List<StoredSymbol> Symbols {
            get {
                // Return symbol list.
                return _Symbols;
            }
        }

    }

    /// <summary>
    /// Helper class to store a state action.
    /// </summary>
    public sealed class StoredStateAction {

        // Data.
        private int? _Action;
        private List<StoredReduceAction> _ReduceActions;

        /// <summary>
        /// Initialize a new stored state action, binding it to the specified action.
        /// </summary>
        /// <param name="act">Action to bind to.</param>
        public StoredStateAction(int? act) {
            // Store data.
            _Action = act;
        }

        /// <summary>
        /// Fetch the action bound to this stored state.
        /// </summary>
        internal int? Action {
            get {
                // Return action.
                return _Action;
            }
        }

        /// <summary>
        /// Fetch list of attached reduce actions.
        /// </summary>
        internal List<StoredReduceAction> ReduceActions {
            get {
                // Return list of reduce actions.
                return _ReduceActions ?? new List<StoredReduceAction>();
            }
        }

        /// <summary>
        /// Attach the specified reduce action to the current action.
        /// </summary>
        /// <param name="redact">Reduce action to attach.</param>
        public void Attach(StoredReduceAction redact) {
            // Check for list and attach.
            if( _ReduceActions == null )
                _ReduceActions = new List<StoredReduceAction>();
            _ReduceActions.Add(redact);
        }

    }

    /// <summary>
    /// Helper class to store a reduce action.
    /// </summary>
    public sealed class StoredReduceAction {

        // Data.
        private StoredSymbol _Symbol;
        private int _Production;

        /// <summary>
        /// Initialize a new stored reduce action with the specified arguments.
        /// </summary>
        /// <param name="symb">Symbol reduce on.</param>
        /// <param name="prod">Production to reduce on.</param>
        public StoredReduceAction(StoredSymbol symb, int prod) {
            // Store data.
            _Symbol = symb;
            _Production = prod;
        }

        /// <summary>
        /// Fetch attached symbol on this reduce action.
        /// </summary>
        internal StoredSymbol Symbol {
            get {
                // Return attachted symbol.
                return _Symbol;
            }
        }

        /// <summary>
        /// Fetch production associated with this reduce action.
        /// </summary>
        internal int Production {
            get {
                // Fetch associated production.
                return _Production;
            }
        }

    }

}
